package br.com.estudante.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.servlet.annotation.WebFilter;

/**
 * As unidades de persistência devem ser inicializadas antes de serem utilizadas
 * e finalizadas quando não forem mais necessárias.
 * 
 * Os filtros de uma aplicação web Java são inicializados automaticamente depois 
 * que a aplicação é implantada no Web Container e antes da primeira requisição HTTP. 
 * Além disso, eles são finalizados ao término da execução da aplicação.
 * 
 */

/**
 * Um filtro pode ser registrado no Web Container através da anotação @WebFilter. 
 * Com essa anotação, podemos definir qual servlet será associada ao filtro. 
 * No exemplo acima, o filtro definido pela classe JPAFilter foi associado a 
 * servlet Faces Servlet.
 *
 */
@WebFilter(servletNames = {"Faces Servlet"})
public class JPAFilter implements Filter{

	private EntityManagerFactory factory;

	/**
	 * O método init() é chamado automaticamente na inicialização do filtro. 
	 * No exemplo abaixo, esse método inicializa a unidade de persistência Estudante-PU.
	 * 
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.factory = Persistence.createEntityManagerFactory("Estudante-PU");		
	}	

	/**
	 * No exemplo abaixo, o método doFilter() é chamado toda vez que uma requisição 
	 * é realizada para a servlet Faces Servlet. Antes de repassar a requisição para
	 * a Faces Servlet, o método doFilter() cria um EntityManager, armazena-o na 
	 * requisição e abre uma transação. Depois que a Faces Servlet processou a requisição,
	 * o método doFilter() tenta confirmar a transação através do método commit(). 
	 * Se um erro ocorrer nessa tentativa, o método rollback() é chamado para cancelar a
	 * transação.
	 * 
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {		
		//chegada
		EntityManager manager = this.factory.createEntityManager();
		request.setAttribute("EntityManager", manager);
		manager.getTransaction().begin();
		
		//faces servlet
		filterChain.doFilter(request, response);
		
		//saída
		try {
			manager.getTransaction().commit();
		} catch (Exception e) {
			manager.getTransaction().rollback();			
		} finally{
			manager.close();
		}		
	}
	
	/**
	 * O método destroy() é chamado automaticamente para desativar o filtro no 
	 * encerramento da aplicação. No exemplo abaixo, finalizamos a unidade de 
	 * persistência Estudante-PU.
	 * 
	 */
	@Override
	public void destroy() {
		this.factory.close();		
	}
	
}
